/************************************************************************
 * Este archivo, representa metodos y variables relacionadas conel alta 
 * baja y modificacion de las estadias de vehiculos
 * 
 * @system: veco-system
 * @description: Sistema de control vehicular
 * @version: 1.0
 * @Copyright © 2013 
 * 
 * @authores:
 * 
 *      > Alberione, Fernando    <falberione_037@gmail.com>
 * 
 *      > Boretto, Martin        <martinboretto@gmail.com>
 * 
 *      > Lenarduzzi, Federico   <fedeatenas@gmail.com>
 * 
 *      > Ocampo, Emanuel        <eocampo86@gmail.com>
 *
 *************************************************************************/

package SistemaCocheras;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.Calendar;
import java.util.HashSet;


public class manejadorEstadia{
    
    private Connection conexion = null;
    private String tiempoEstadia = "";

    
    // ---------------------------------------------------------
    // ------- Seccion de constructores de la clase
    // ---------------------------------------------------------      

    /**
     * Constructor vacio
     */
    public manejadorEstadia(Connection c){
        this.conexion = c;
    }

    
    
    // ---------------------------------------------------------
    // ------- Seccion de metodos publicos auxiliares
    // --------------------------------------------------------- 
    
    /**
     * Metodo encargado de obtener el tiempo de estadia
     * @return tiempoEstadia
     */
    public String obtenerTiempoEstadia(){
        return this.tiempoEstadia;
    }// Fin obtenerTiempoEstadia
    

    /**
     * Metodo encargado de darle el alta a la estadia de un vehiculo
     * @param fechaIngreso Representa la fecha en la que ingreso el vehiculo
     * @param hsActual Representa la hora en la que ingreso el vehiculo
     * @param dominio Representa el dominio del vehiculo
     * @param posicion Representa la posicion asignada al vehiculo en la cochera
     * @param tipo Representa el tipo de vehiculo
     * @throws IOException
     * @throws SQLException 
     */
    public void marcarEntrada(String fechaIngreso, String hsActual, String dominio, int posicion, 
                              int tipo) throws IOException,SQLException {
        Statement statemen = this.conexion.createStatement();
        if (!buscarEstadia(dominio).next()){
          String query = "INSERT INTO estadia (fechaingreso,horaingreso,dominiovehiculo,estaadentro,lugarcochera,tipovehiculo) VALUES('"+fechaIngreso+"', '"+hsActual+"', '"
                         +dominio.toUpperCase()+"','1','"+posicion+"', '"+tipo+"')";
          statemen.executeUpdate(query);
        }
    }// Fin marcarEntrada
    
    
    /**
     * Metodo encargado de marcar la salida de un determinado cliente
     * @param fechaSalida Representa la fecha de salida del cliente
     * @param hsSalida Representa la hora de salida del cliente
     * @param dominio Representa el dominio del vehiculo del cliente
     * @return 0.0
     * @throws IOException
     * @throws SQLException 
     */
    public double marcarSalidaCliente(String fechaSalida, String hsSalida, String dominio) throws IOException,SQLException  {
        Statement statemen = this.conexion.createStatement();
        ResultSet res = buscarEstadia(dominio);
        if (res.next()){
            String query = "UPDATE estadia SET fechasalida = '" +fechaSalida+
                              "', horasalida = '" +hsSalida+
                              "', monto = '" +0+
                              "', estaadentro = '0' WHERE (dominiovehiculo = '" +dominio.toUpperCase()+"')";
            statemen.executeUpdate(query);
        }
        return 0.0;
    }// Fin marcarSalidaCliente
    
    
    /**
     * Metodo encargado de darle de baja a una estadia de un vehiculo
     * @param fechaSalida Representa la fecha de salida del vehiculo
     * @param hsSalida Representa la hora de salida del vehiculo
     * @param dominio Representa el dominio del vehiculo
     * @param tarifaP Representa el tipo de tarifa
     * @return monto
     * @throws IOException
     * @throws SQLException 
     */
    public double marcarSalida(String fechaSalida, String hsSalida, String dominio, Double[] tarifaP) throws IOException,SQLException  {
        Statement statemen = this.conexion.createStatement();
        // Se busca en la cochera el vehiculo a dar de baja
        ResultSet res = buscarEstadia(dominio);
        double monto = 0.0;
        if (res.next()){
            monto = calcularMonto(res, fechaSalida, hsSalida, tarifaP);
            String query = "UPDATE estadia SET fechasalida = '" +fechaSalida+
                            "', horasalida = '" +hsSalida+
                            "', monto = '" +monto+
                            "', estaadentro = '0' WHERE (dominiovehiculo = '" +dominio.toUpperCase()+"')";
            statemen.executeUpdate(query);
        }
        return monto;
    }// Fin marcarSalida
   
    
    /**
     * Metodo encargado de obtener todas las posiciones ocupadas de la cochera
     * @return res
     * @throws SQLException 
     */
    public ResultSet obtenerPosicionesOcupados() throws SQLException{
        ResultSet res = null;
        Statement statemen = this.conexion.createStatement();
        String query = "SELECT (lugarcochera) FROM estadia WHERE estaadentro = '1'";
        res = statemen.executeQuery(query);
        return res;
    }// Fin obtenerPosicionesOcupados

    
    /**
     * Metodo encargado de controlar si existen lugares disponibles
     * @param ocupados Representa los lugares ocupados
     * @param posicion Representa la posicion a chequear
     * @return true o false
     * @throws SQLException 
     */
    public boolean hayLugarDisponible(ResultSet ocupados, int posicion) throws SQLException{
        HashSet conjunto = new HashSet();
        while (ocupados.next()){
            conjunto.add(ocupados.getInt(1));
        }
        return (!conjunto.contains(posicion));
    }// Fin hayLugarDisponible    
    
    
    /**
     * Metodo encargado de buscar en la base de datos el vehiculo con dominio
     * pasado como parametro
     * @param dominio Representa el dominio del vehiculo a buscar
     * @return res
     * @throws SQLException 
     */
    public ResultSet buscarEstadia(String dominio)throws SQLException {
        ResultSet res = null;
        Statement statemen = this.conexion.createStatement();
        String query = "SELECT * FROM estadia WHERE(dominiovehiculo ='"+dominio+"' and estaadentro='1')";
        res = statemen.executeQuery(query);
        return res;
    }// Fin buscarEstadia
    

    /**
     * Metodo encargado de buscar todos los lugares ocupados de la cochera
     * @return res
     * @throws SQLException 
     */
    public ResultSet buscarEstadiaOcupado() throws SQLException {
        ResultSet res = null;
        Statement statemen = this.conexion.createStatement();
        String query = "SELECT * FROM estadia WHERE(estaadentro='1') ORDER BY lugarcochera asc";
        res = statemen.executeQuery(query);
        return res;
    }// Fin buscarEstadiaOcupado

    
    /**
     * Metodo encargado de retornar la estadia de vehiculos correspondientes a 
     * una fecha de ingreso y una fecha de egreso determinada
     * @param fIngreso Representa la fecha de ingreso del rango
     * @param fEgreso Representa la fecha de egreso del rango
     * @return res
     * @throws SQLException 
     */
    public ResultSet buscarEstadiaRango(String fIngreso, String fEgreso) throws SQLException {
        ResultSet res = null;
        Statement statemen = this.conexion.createStatement();
        String query = "SELECT * FROM estadia WHERE(fechaingreso>= '"+fIngreso+"' and fechasalida<= '"+fEgreso+"')";
        res = statemen.executeQuery(query);
        return res;
    }// Fin buscarEstadiaRango

    
    /**
     * Metodo encargado de retonar la estadia de un determinado vehiculo correspondiente a 
     * una fecha de ingreso y una fecha de egreso determinada
     * @param fIngreso Representa la fecha de ingreso del rango
     * @param fEgreso Representa la fecha de egreso del rango
     * @param dominio Representa el dominio del vehiculo
     * @return res
     * @throws SQLException 
     */
    public ResultSet buscarEstadiaRangoYPatente(String fIngreso, String fEgreso, String dominio) throws SQLException {
        ResultSet res = null;
        Statement statemen = this.conexion.createStatement();
        String query = "SELECT * FROM estadia WHERE(fechaingreso>= '"+fIngreso+"' and fechasalida<= '"+fEgreso+"' and dominiovehiculo<= '"+dominio+"')";
        res = statemen.executeQuery(query);
        return res;
    }// Fin buscarEstadiaRangoYPatente

    
    /**
     * Metodo encargado de controlar si un anio es o no bisiesto
     * @param anio Representa el anio a controlar
     * @return true o false
     */
    public boolean esBisiesto(int anio) {
        return ((anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0);
    }// Fin esBisiesto

    
    /**
     * Metodo encargado de calcular la fecha del sistema
     * @return anio-mes-dia
     */
    public String calcularFecha (){
        Date date = new Date();
        int year = date.getYear() + 1900;
        int month = date.getMonth() + 1;
        int day = Calendar.getInstance().get(Calendar.DATE);
        return (""+year+"-"+month+"-"+day);
    }// Fin calcularFecha


    /**
     * Metodo encargado de calcular la Hora del sistemaS
     * @return hora:min:seg
     */
    public String calcularHora(){
      Date date = new Date();
      int hs = date.getHours();
      return (""+hs+":"+date.getMinutes()+":"+date.getSeconds());
    }// Fin calcularHora


    /**
     * Metodo encargado de obtener la tarifa correspondiente a una hora
     * @return tarifa
     * @throws SQLException 
     */
    public double obtenerTarifa() throws SQLException {
        manejadorCochera mc = new manejadorCochera(this.conexion);
        ResultSet cochera = mc.buscarCochera();
        cochera.next();
        // Se obtiene el valor del registro corriente de la columna "tarifahs"
        Double tarifa = Double.parseDouble(cochera.getString("tarifahs"));          
        return tarifa;
    }// Fin obtenerTarifa


    /**
     * Metodo encargado de obtener el numero total de clientes de la cochera
     * @return cantClientes
     * @throws SQLException 
     */
    public int obtenerNumeroDeClientes() throws SQLException{
        int cantClientes = 0;
        ResultSet res = null;
        Statement statemen = this.conexion.createStatement();
        String query = "SELECT COUNT(dni) FROM persona";
        res = statemen.executeQuery(query);
        if(res.next()){
            cantClientes = res.getInt(1);
        }
        return cantClientes;
    }// Fin obtenerNumeroDeClientes


    /**
     * Metodo encargado de obtener todos los lugares ocupados de la cochera
     * @return cantLugares
     * @throws SQLException 
     */
    public int obtenerLugaresOcupados() throws SQLException{
        int cantLugares = 0;
        int cantClientes = obtenerNumeroDeClientes();
        ResultSet res = null;
        Statement statemen = this.conexion.createStatement();
        String query = "SELECT COUNT(fechaIngreso) FROM estadia WHERE estaadentro = '1'";
        res = statemen.executeQuery(query);
        if(res.next()){
            cantLugares = res.getInt(1);
        }
        return (cantLugares + cantClientes);
    }// Fin obtenerLugaresOcupados


    /**
     * Metodo encargado de obtener los lugares disponibles de la cochera
     * @return lugaresDisponibles
     * @throws SQLException 
     */
    public int obtenerLugaresDisponibles() throws SQLException{
        int cantLugares = 0;
        int cantOcupados = obtenerLugaresOcupados();
        ResultSet res = null;
        Statement statemen = this.conexion.createStatement();
        String query = "SELECT cantidad FROM cochera WHERE id_cochera = '1';";
        res = statemen.executeQuery(query);
        if(res.next()){
            cantLugares = res.getInt(1);
        }
        return (cantLugares - cantOcupados);
    }// Fin obtenerLugaresDisponibles
    

    /**
     * Metodo encargado de devolver la proxima posicion libre de la cochera
     * @param ocupados Representa a los lugares ocupados
     * @param capacidad Representa la capacidad de la cochera
     * @return resultado
     * @throws SQLException 
     */
    public int calcularProximaPosicion(ResultSet ocupados, int capacidad) throws SQLException{
        int resultado = 1;
        boolean bandera = false;
        HashSet conjunto = new HashSet();
        while (ocupados.next()){
            conjunto.add(ocupados.getInt(1));
        }
        if (!conjunto.isEmpty()){
            while (resultado <= capacidad && (!bandera)){
                if (!conjunto.contains(resultado)){
                    bandera = true;
                }
                else{
                    resultado++;
                }
            }
            if (!bandera){
                resultado = 0;
            }
        }
        return resultado;
    }// Fin calcularProximaPosicion
    
    
    
    // ---------------------------------------------------------
    // ------- Seccion de metodos privados
    // ---------------------------------------------------------    
    
    /**
     * Metodo encargado de calcular el monto a partir de un vehiculo determinado
     * @param res Representa el vehiculo a calcularle el monto de su estadia
     * @param fechaEgreso Representa la fecha de egreso del vehiculo
     * @param hsEgreso Representa la hora de egreso del vehiculo
     * @param tarifa Representa el tipo de tarifa
     * @return monto
     * @throws SQLException 
     */
    private double calcularMonto(ResultSet res, String fechaEg, String hsEgreso, Double[] tarifa) throws SQLException {
        String fechaIng = res.getObject(1).toString();
        String horaIng = res.getObject(3).toString();
        String [] fIngParser = fechaIng.split("-");
        String [] fEgParser = fechaEg.split("-");
        String [] hsIngParser = horaIng.split(":");
        String [] hsEgParser = hsEgreso.split(":");
        int [] fIng = new int[3];
        int [] fEg = new int[3];
        int [] hsIng = new int[3];
        int [] hsEg = new int[3];
        for (int i = 0 ; i<3; i++){
            fIng[i] = Integer.parseInt(fIngParser[i]);
            fEg[i] = Integer.parseInt(fEgParser[i]);
            hsIng[i] = Integer.parseInt(hsIngParser[i]);
            hsEg[i] = Integer.parseInt(hsEgParser[i]);
        }
        Date fechaIngreso = new Date(fIng[0], fIng[1], fIng[2], hsIng[0], hsIng[1], hsIng[2]);
        Date fechaEgreso = new Date(fEg[0], fEg[1], fEg[2], hsEg[0], hsEg[1], hsEg[2]);
        long ing = fechaIngreso.getTime();
        long eg = fechaEgreso.getTime();
        long diferencia = eg - ing;
        
        // Calculamos los dias de estadia que estuvo el vehiculo
        int diasDeEstadia = Math.round((diferencia/(((1000*60)*60)*24)));
        
        // Calculamos las horas de estadia que estuvo el vehiculo
        int horasDeEstadia = Math.round((diferencia/(1000*60*60))) - (diasDeEstadia*24);
        
        // Calculamos los minutos de estadia que estuvo el vehiculo
        int minDeEstadia = ((Math.round((diferencia/(1000*60)))) - (horasDeEstadia*60) - ((diasDeEstadia*60)*24));
        
        // Utilizada para mostrar en el cartel de salida
        this.tiempoEstadia = horasDeEstadia+" hs "+minDeEstadia+" min";
        
        if( fIng[1] < fEg[1] ){
            // Controlamos que si una estadia esta entre el limite de un mes y el siguiente, 
            // se reste un dia (debido a que sino, cobra el monto por un dia mas)
            diasDeEstadia--;
        }                    
        manejadorCochera mc = new manejadorCochera(this.conexion);
        ResultSet cochera = mc.buscarCochera();
        cochera.next();
        double precio_hora = tarifa[2];
        double precio_dia = tarifa[3];
        int min_permitidos = Integer.parseInt(cochera.getString("max_min_perm"));
        // Calculo del precio en minutos correspondiente a la estadia del vehiculo
        double precioMinEstadia = calcularPreciosMinutosEstadia(minDeEstadia, tarifa[0], tarifa[1], precio_hora, horasDeEstadia, min_permitidos);
        return ((diasDeEstadia*precio_dia) + (horasDeEstadia*precio_hora) + precioMinEstadia);
    }// Fin calcularMonto       
   
    
    /**
     * Funcion encargada de calcular cuanto se le cobra a un determinado vehiculo de acuerdo a los
     * minutos de estadia que estuvo
     * @param minEstadia Representan los minutos de estadia que estuvo el vehiculo
     * @param pQuince Representa el valor del precio por quince minutos
     * @param pTreinta Representa el valor del precio por treinta minutos
     * @param pHora Representa el valor del precio por una hora
     * @param hEstadia Representa las horas de estadia del vehiculo
     * @param minPerm Representa los minutos permitidos en la cochera
     * @return precioMinEstadia
     */
    private double calcularPreciosMinutosEstadia(int minEstadia, double pQuince, double pTreinta, double pHora, 
                                                 int hEstadia, int minPerm){
        final int quinceMin = 15;
        final int treintaMin = 30;
        double precioMinEstadia = 0.0;
        if(minEstadia <= minPerm){
            // La estadia es de menos del tiempo margen
            precioMinEstadia = 0.0;
        }
        else{
            if(minEstadia <= (quinceMin + minPerm)){
                // La estadia es de 15 minutos
                precioMinEstadia = pQuince;
            }
            else{
                if(minEstadia <= (treintaMin + minPerm)){
                    // La estadia es de 30 minutos
                    if(hEstadia != 0){
                        // Caso de cobro de mas de hora
                        pTreinta = pHora/2;
                    }
                    precioMinEstadia = pTreinta;  
                }
                else{
                    // La estadia corresponde a otra hora (paso los 35 minutos)
                    precioMinEstadia = pHora;
                }
            }
        }
        return precioMinEstadia;
    }// Fin calcular_precios_minutos_estadia
    
    
    
    
}// Fin class manejadorCliente
